Support vendor dirs with "empty" directories
authorAlex Crichton <alex@alexcrichton.com>
Tue, 4 Apr 2017 23:28:42 +0000 (16:28 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 4 Apr 2017 23:28:42 +0000 (16:28 -0700)
Looks like when vendor directories are checked into a VCS there's been instances
where deleting a folder in the VCS doesn't fully delete the folder from the
filesystem. This can lead to [spurious errors][moz] that are difficult to debug.

To help handle this tweak directory sources to ignore empty directories or
directories with only dotfiles by default.

[moz]: https://bugzilla.mozilla.org/show_bug.cgi?id=1342292

src/cargo/sources/directory.rs
tests/directory.rs

index 1cf8b27550bdc4cde0928680f45539747227a2b0..ee27a13cec3c831c57cf9029b9b5afbf925e92e7 100644 (file)
@@ -81,6 +81,32 @@ impl<'cfg> Source for DirectorySource<'cfg> {
                 }
             }
 
+            // Vendor directories are often checked into a VCS, but throughout
+            // the lifetime of a vendor dir crates are often added and deleted.
+            // Some VCS implementations don't always fully delete the directory
+            // when a dir is removed from a different checkout. Sometimes a
+            // mostly-empty dir is left behind.
+            //
+            // To help work Cargo work by default in more cases we try to
+            // handle this case by default. If the directory looks like it only
+            // has dotfiles in it (or no files at all) then we skip it.
+            //
+            // In general we don't want to skip completely malformed directories
+            // to help with debugging, so we don't just ignore errors in
+            // `update` below.
+            let mut only_dotfile = true;
+            for entry in path.read_dir()?.filter_map(|e| e.ok()) {
+                if let Some(s) = entry.file_name().to_str() {
+                    if s.starts_with(".") {
+                        continue
+                    }
+                }
+                only_dotfile = false;
+            }
+            if only_dotfile {
+                continue
+            }
+
             let mut src = PathSource::new(&path, &self.source_id, self.config);
             src.update()?;
             let pkg = src.root_package()?;
index 33fd9a039e585d00f4912d7b6c61231347ff88a1..8ff54c1476eb6da1afc72a3608013870c239ad48 100644 (file)
@@ -471,3 +471,36 @@ required then it is recommended that [replace] is used with a forked copy of \
 the source
 "));
 }
+
+#[test]
+fn only_dot_files_ok() {
+    setup();
+
+    VendorPackage::new("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.1.0"
+            authors = []
+        "#)
+        .file("src/lib.rs", "")
+        .build();
+    VendorPackage::new("bar")
+        .file(".foo", "")
+        .build();
+
+    let p = project("bar")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "bar"
+            version = "0.1.0"
+            authors = []
+
+            [dependencies]
+            foo = "0.1.0"
+        "#)
+        .file("src/lib.rs", "");
+    p.build();
+
+    assert_that(p.cargo("build"), execs().with_status(0));
+}